package com.conding365.gateway.server.filter;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class IpFilter implements GlobalFilter,Ordered {

    private Map<String,List<Long>> ipMap = new ConcurrentHashMap<>();

    //反IP爆刷时间间隔
    @Value("${ip.xx}")
    private Integer x;
    @Value("${ip.yy}")
    private Integer y;

    private Integer index = 0;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 从上下⽂中取出request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String ip = getIP(request);
        Date now = new Date();
        if (request.getPath().value().startsWith("/user/register")){
            List<Long> accessTimeList = ipMap.get(ip);
            if (accessTimeList == null){
                accessTimeList = new ArrayList<>();
                ipMap.put(ip,accessTimeList);
            }
            if (accessTimeList.size() < y){
                accessTimeList.add(now.getTime());
            }else {
                accessTimeList.set(index % y,now.getTime());
            }
            //获取y次中最早的访问时间
            Long earlyTime = 0L;
            if (index == y + 1){
                earlyTime = accessTimeList.get(0);
            }else if (index > y + 1){
                int early = (index + 1) % y;
                earlyTime = accessTimeList.get(early);
            }
            //x分钟内超过y次访问，将被限制
            if (now.getTime() - earlyTime < x * 60 * 1000){
                System.out.println("IP("+ ip + ")防爆刷====>"+x + "分钟内，访问注册接口不能超过" + y + "次!!!");
                response.setStatusCode(HttpStatus.FORBIDDEN);
                String data = "Request be denied!";
                DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
                return response.writeWith(Mono.just(wrap));
            }

            index++;

        }
        return chain.filter(exchange);
    }

    private static final String IP_UNKNOWN = "unknown";
    private static final String IP_LOCAL = "127.0.0.1";
    private static final int IP_LEN = 15;

    public static String getIP(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String ipAddress = headers.getFirst("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) {
            ipAddress = headers.getFirst("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) {
            ipAddress = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) {
            ipAddress = Optional.ofNullable(request.getRemoteAddress())
                    .map(address -> address.getAddress().getHostAddress())
                    .orElse("");
            if (IP_LOCAL.equals(ipAddress)) {
                // 根据网卡取本机配置的IP
                try {
                    InetAddress inet = InetAddress.getLocalHost();
                    ipAddress = inet.getHostAddress();
                } catch (UnknownHostException e) {
                    // ignore
                }
            }
        }

        // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if (ipAddress != null && ipAddress.length() > IP_LEN) {
            int index = ipAddress.indexOf(",");
            if (index > 0) {
                ipAddress = ipAddress.substring(0, index);
            }
        }
        return ipAddress;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
